package com.aabte.auth.account.application;

import com.aabte.auth.account.domain.exception.AccountAccountOrPasswordException;
import com.aabte.auth.account.domain.exception.AccountExistException;
import com.aabte.auth.account.domain.exception.AccountNotExistException;
import com.aabte.auth.account.domain.model.Account;
import com.aabte.auth.account.domain.model.AccountId;
import com.aabte.auth.account.domain.model.AccountName;
import com.aabte.auth.account.domain.model.Password;
import com.aabte.auth.account.domain.repository.AccountRepository;
import com.aabte.auth.account.domain.service.PasswordService;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Optional;

@Service
public class AccountService {

    @Resource
    private AccountRepository accountRepository;

    @Resource
    private PasswordService passwordService;

    @Resource
    private AccountEventPublisher accountEventPublisher;

    @Transactional(rollbackFor = Exception.class)
    public AccountId createNewAccount(AccountName accountName, Password password) {

        Optional<Account> accountOptional = accountRepository.find(accountName);
        if (accountOptional.isPresent()) {
            throw new AccountExistException("accountName: " + accountName.getName());
        }

        Account account = Account.builder()
                .accountId(accountRepository.nextAccountId())
                .accountName(accountName)
                .accountPassword(passwordService, password.getPassword())
                .build();

        accountRepository.addOrUpdate(account);

        accountEventPublisher.accountCreated(account);

        return account.getAccountId();
    }

    @Transactional(rollbackFor = Exception.class)
    public void updatePassword(AccountName accountName, Password currentPassword, Password newPassword) {
        Password encryption = Password.encryption(passwordService, currentPassword);
        Optional<Account> accountOptional = accountRepository.find(accountName, encryption);
        if (!accountOptional.isPresent()) {
            throw new AccountAccountOrPasswordException("accountName: " + accountName.getName());
        }
        Account account = accountOptional.get();
        account.updatePassword(passwordService, newPassword);
        accountRepository.addOrUpdate(account);

        accountEventPublisher.passwordChanged(account);
    }

    public Account account(AccountName accountName, Password password) {
        Optional<Account> accountOptional = accountRepository.find(
                accountName, Password.encryption(passwordService, password));
        if (!accountOptional.isPresent()) {
            throw new AccountAccountOrPasswordException("accountName: " + accountName.getName());
        }
        return accountOptional.get();
    }

    public Page<Account> accounts(Pageable pageable) {
        return accountRepository.find(pageable);
    }

    public Account account(AccountId accountId) {
        Optional<Account> accountOptional = accountRepository.find(accountId);
        if (accountOptional.isPresent()) {
            return accountOptional.get();
        }
        throw new AccountNotExistException("accountId: " + accountId.getId());
    }
}
